package org.dt.common.sequence;

import lombok.ToString;

import java.util.concurrent.atomic.AtomicLong;

/**
 * sequence that track the last assigned number
 *
 * @author yujiadong
 * @create: 2019/01/08 14:10
 */
@ToString(callSuper = true, doNotUseGetters = true)
public class TrackedSequence extends Sequence {
    static final         TrackedSequence EMPTY            = new TrackedSequence(0, -1);
    private static final long            serialVersionUID = -7266936318274834108L;
    /**
     * last assigned number
     */
    private final        AtomicLong      last;

    TrackedSequence(long lowerBound, long upperBound) {
        super(lowerBound, upperBound);
        this.last = new AtomicLong(lowerBound - 1);
    }

    public static TrackedSequence from(Sequence sequence) {
        return new TrackedSequence(sequence.lowerBound, sequence.upperBound);
    }

    long availableWidth() {
        return upperBound - last.get();
    }

    boolean isEmpty() {
        return last.get() >= upperBound;
    }

    Sequence tryConsume(Long need) {
        final AtomicLong lastAtomic = this.last;
        long             lastPoint, nextLast, nextValidLast;
        while (true) {
            lastPoint = lastAtomic.get();
            if (lastPoint == upperBound) {
                return Sequence.EMPTY;
            }

            nextLast      = lastPoint + need;
            nextValidLast = nextLast > upperBound ? upperBound : nextLast;
            if (lastAtomic.compareAndSet(lastPoint, nextValidLast)) {
                return new Sequence(lastPoint + 1, nextValidLast);
            }

        }
    }
}